package com.imooc.ecommerce.service.communication;

import com.imooc.ecommerce.vo.JwtToken;
import com.imooc.ecommerce.vo.UsernameAndPassword;
import feign.Feign;
import feign.Logger;
import feign.gson.GsonDecoder;
import feign.gson.GsonEncoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.support.SpringMvcContract;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.ThreadLocalRandom;

/**
 * <p>使用 Feign 的原生 Api, 而不是 OpenFeign = Feign + Ribbon
 *
 * @author Yaxi.Zhang
 * @since 2022/11/13 10:49
 */
@Slf4j
@Service
public class UseFeignApi {

    private final DiscoveryClient discoveryClient;

    public UseFeignApi(DiscoveryClient discoveryClient) {
        this.discoveryClient = discoveryClient;
    }

    /**
     * <p>使用 Feign 原生 api 调用远端服务
     * <p>Feign 默认配置初始化、设置自定义配置、生成代理对象
     */
    public JwtToken thinkingInFeign(UsernameAndPassword usernameAndPassword) {
        // 通过反射去拿 serviceId
        String serviceId = null;
        FeignClient anno = AuthorityFeignClient.class.getAnnotation(FeignClient.class);
        if (Objects.nonNull(anno)) {
            serviceId = anno.value();
            log.info("get service id from AuthorityFeignClient: [{}]", serviceId);
        }

        // 如果服务 id 不存在, 直接抛异常
        if (Objects.isNull(serviceId)) {
            throw new RuntimeException("can not get serviceId");
        }

        // 通过 serviceId 去拿可用服务实例
        List<ServiceInstance> targetInstances = discoveryClient.getInstances(serviceId);
        if (CollectionUtils.isEmpty(targetInstances)) {
            throw new RuntimeException("can not get target instance from serviceId: " + serviceId);
        }

        // 随机选择一个服务实例: 负载均衡
        ServiceInstance randomInstance = targetInstances.get(ThreadLocalRandom.current().nextInt(targetInstances.size()));
        log.info("choose service instance in thinkingInFeign: [{}], [{}], [{}]", serviceId, randomInstance.getHost(), randomInstance.getPort());

        // Feign 客户端初始化, 必须要配置 encoder、decoder、contract
        AuthorityFeignClient feignClient = Feign.builder()
                .encoder(new GsonEncoder())
                .decoder(new GsonDecoder())
                .logLevel(Logger.Level.FULL)
                .contract(new SpringMvcContract())
                .target(AuthorityFeignClient.class, String.format("http://%s:%s", randomInstance.getHost(), randomInstance.getPort()));

        return feignClient.getTokenByFeign(usernameAndPassword);
    }
}
